#include  <stdio.h>
#include  <stdlib.h> // for rand()
#include  "pcb.h"

/* Mora da se definise u test-u */
void tick() {}

int semPreempt = 0;

#define PUT_NEWLINE() putchar('\n')

#define TRUE    1
#define FALSE   0
/** 
 * @brief Proverava da li sve niti imaju jedinstven UID
 */
static short    UNIQ_UID        = TRUE;
/** 
 * @brief Ako je ova opcija omogucena onda test program obavestava korisnika o
 * svakom ucinjenom koraku.
 *
 * npr. Stampa stanje niza svih niti pre i posle dodavanja/brisanja niti iz
 * sistema
 */
static short    SET_VERBOSE     = TRUE;
/** 
 * @brief Proverava brisanje poslednjeg clana u nizu niti
 *
 */
static short    LAST_DEL        = TRUE;
/** 
 * @brief Ako je zbir svih UID koje su stvoreni i izbrisani isti onda je
 * razumno pretpostaviti da su sve niti koje su stvorene takodje i izbrisane
 */
static short    COUNT_UID       = TRUE;
static long     uid_check_counter = 0;


void
print_pcb(int i) {
    printf("[%d]-UID={%d}", i, PCB::all_pcbs[i]->uid);
}

void
print_pcbs() {
    int i;

    for (i = 0; i < PCB::last_index ; i++) {
        print_pcb( i );
        putchar( ';' );
    }
    PUT_NEWLINE();
}

void
check_uid() {
    int i;
    int j;
    int uid_temp;

    for (i = 0; i < PCB::last_index ; i++) {
        uid_temp = PCB::all_pcbs[i]->uid;
        for (j = 0; j < PCB::last_index ; j++) {
            if (PCB::all_pcbs[j]->uid == uid_temp && j != i) {
                UNIQ_UID = FALSE;
                if (!SET_VERBOSE)
                    return;

                if (SET_VERBOSE) {
                    printf(">> Imamo duplikat: ");
                    print_pcb( j );
                    putchar( ';' );
                    print_pcb( i );
                    PUT_NEWLINE();
                }
            }
        }

    }
}
void
delete_pcbs() {
    int i;
    /* Poslednji element u listi, index za jedan veci od poslednjeg
     * upotrebljenog */
    int last;

    long deleted_uid_sum = 0;

    check_uid();

    last = PCB::last_index;
    /* Testiram brisanje poslednjeg elementa */

    if ( last != 0 ) {
        deleted_uid_sum += PCB::all_pcbs[last - 1]->uid;
        PCB::del_from_list(PCB::all_pcbs[last - 1]);

        /* Nije dobro obrisao poslednji element */
        if (PCB::all_pcbs[last - 1] != NULL)
            LAST_DEL = FALSE;
        last--;
    }

    int count = 0;
    while(1) {
        // Ako je lista prazna
        if (PCB::all_pcbs[0] == NULL)
            break;

        /* Generisem sledeci element za brisanje, rand() % 1 nikad nece biti
         * nula, pa moram eksplicitno da mu promenim vrednost */
        i = (last != 1)
            ?rand() % last
            : 0;
        printf("> Brisem: ");
        print_pcb( i );
        PUT_NEWLINE();

        if (SET_VERBOSE) {
            printf("Pre: \n");
            print_pcbs();
        }

        count++;
        deleted_uid_sum += PCB::all_pcbs[i]->uid;
        PCB::del_from_list(PCB::all_pcbs[i]);
        last--;

        if (SET_VERBOSE) {
            printf("Posle: \n");
            print_pcbs();
        }
        printf("deleted counter = %d\n", deleted_uid_sum);
    }
    if (deleted_uid_sum != uid_check_counter) {
        printf("check counter = %ld, deleted counter = %ld, count = %d\n", uid_check_counter, deleted_uid_sum, count);
        COUNT_UID = FALSE;
    }
}

void
check_message() {
    char passed[]        = "PASSED";
    char not_passed[]    = "NOT PASSED";

    printf("STATUS TESTOVA:\n");
    printf("# UNIQ_UID: %s\n", (UNIQ_UID)? passed: not_passed);
    printf("# LAST_DEL: %s\n", (LAST_DEL)? passed: not_passed);
    printf("# COUNT_UID: %s\n", (COUNT_UID)? passed: not_passed);

}
int main(int argc, char *argv[])
{
    PCB *tmp = NULL;
    int i;

    SET_VERBOSE = FALSE;
    srand(1212);

    for (i = 0; i < MAX_PROC; i++) {
        tmp = new PCB( (Thread*)NULL, (UID)0, (Priority) 0, (StackSize) 0, (Time) 1);
        uid_check_counter += tmp->uid;
    }
    printf("> Svi PCB-ovi u globalnom nizu: \n");
    print_pcbs();
    delete_pcbs();

    check_message();

    return 0;
}
